package com.zhongen.wflow.workflow.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zhongen.wflow.bean.entity.WflowModelHistorys;
import com.zhongen.wflow.bean.entity.WflowModelPerms;
import com.zhongen.wflow.bean.entity.WflowModels;
import com.zhongen.wflow.bean.vo.CustomPrintConfigVo;
import com.zhongen.wflow.exception.BusinessException;
import com.zhongen.wflow.mapper.WflowModelHistorysMapper;
import com.zhongen.wflow.mapper.WflowModelPermsMapper;
import com.zhongen.wflow.mapper.WflowModelsMapper;
import com.zhongen.wflow.service.OrgRepositoryService;
import com.zhongen.wflow.workflow.WFlowToBpmnCreator;
import com.zhongen.wflow.workflow.bean.process.OrgUser;
import com.zhongen.wflow.workflow.bean.process.ProcessNode;
import com.zhongen.wflow.workflow.bean.process.props.RootProps;
import com.zhongen.wflow.workflow.service.ProcessModelService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.validation.ProcessValidator;
import org.flowable.validation.ProcessValidatorFactory;
import org.flowable.validation.ValidationError;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author : willian fu
 * @date : 2022/8/25
 */
@Slf4j
@Service
public class ProcessModelServiceImpl implements ProcessModelService {

	@Autowired
	private HistoryService historyService;

	@Autowired
	private WflowModelHistorysMapper modelHistorysMapper;

	@Autowired
	private WflowModelsMapper modelsMapper;

	@Autowired
	private RepositoryService repositoryService;

	@Autowired
	private OrgRepositoryService orgRepositoryService;

	@Autowired
	private WflowModelPermsMapper modelPermsMapper;

	@Override
	@Transactional
	public String saveProcess(WflowModelHistorys models) {
		if (ObjectUtil.isNull(models.getFormId())) {
			models.setCreated(GregorianCalendar.getInstance().getTime());
			models.setFormId("wf" + IdUtil.objectId());
			models.setVersion(1);
			WflowModels wflowModels = new WflowModels();
			BeanUtil.copyProperties(models, wflowModels);
			wflowModels.setIsDelete(false);
			wflowModels.setIsStop(false);
			wflowModels.setUpdated(new Date());
			wflowModels.setSort(0);
			modelHistorysMapper.insert(models);
			// 初始的流程在部署表也存一份，用来查询
			modelsMapper.insert(wflowModels);
			log.info("保存流程[{}]初始V1新版本", models.getFormId());
		}
		else {
			// 检查最新版本有没有部署过
			WflowModelHistorys lastVersionModel = getLastVersionModel(models.getFormId());
			if (StrUtil.isNotBlank(lastVersionModel.getProcessDefId())) {
				// 发布过，构建新版
				models.setProcessDefId(null);
				models.setVersion(lastVersionModel.getVersion() + 1);
				models.setCreated(GregorianCalendar.getInstance().getTime());
				modelHistorysMapper.insert(models);
				log.info("保存流程[{}]的新版本，版本号:{}", lastVersionModel.getFormId(), models.getVersion());
			}
			else {
				// 没发布，就覆盖
				models.setId(lastVersionModel.getId());
				models.setCreated(null);
				models.setVersion(null);
				modelHistorysMapper.updateById(models);
				log.info("保存未发布的流程[{}]的，版本号:{}", lastVersionModel.getFormId(), lastVersionModel.getVersion());
			}
		}
		return models.getFormId();
	}

	@Override
	@Transactional
	public void enableProcess(String code, boolean enable) {
		WflowModels wflowModels = modelsMapper.selectById(code);
		modelsMapper.updateById(WflowModels.builder().formId(code).isStop(enable).build());
		try {
			if (enable) {
				repositoryService.activateProcessDefinitionById(wflowModels.getProcessDefId());
			}
			else {
				repositoryService.suspendProcessDefinitionById(wflowModels.getProcessDefId());
			}
		}
		catch (Exception e) {
			log.warn("流程[{}]没有发布过，无需操作", code);
		}
	}

	@Override
	@Transactional
	public String deployProcess(String code) {
		// 从历史表获取最新版本的流程，然后放到发布表再部署
		WflowModelHistorys wflowModels = getLastVersionModel(code);
		if (ObjectUtil.isNull(wflowModels)) {
			log.warn("流程{}不存在", code);
			throw new BusinessException("不存在该表单");
		}
		ProcessNode<?> processNode = JSONObject.parseObject(wflowModels.getProcess(), ProcessNode.class);
		BpmnModel bpmnModel = new WFlowToBpmnCreator().loadBpmnFlowXmlByProcess(wflowModels.getFormId(),
				wflowModels.getFormName(), processNode, false);
		ProcessValidatorFactory processValidatorFactory = new ProcessValidatorFactory();
		ProcessValidator defaultProcessValidator = processValidatorFactory.createDefaultProcessValidator();
		// 验证失败信息的封装ValidationError
		List<ValidationError> validate = defaultProcessValidator.validate(bpmnModel);
		if (CollectionUtil.isNotEmpty(validate)) {
			log.error("流程[{}验证失败]：{}", code, JSONObject.toJSONString(validate));
			throw new BusinessException("流程设计错误:" + validate.stream()
				.map(err -> (err.getActivityId() + ":" + err.getActivityName()))
				.collect(Collectors.joining(",")));
		}
		String xmlString = new String(new BpmnXMLConverter().convertToXML(bpmnModel));
		// 流程部署
		log.debug("流程生成bpmn-xml为：{}", xmlString);
		Deployment deploy = repositoryService.createDeployment()
			.key(code)
			.name(wflowModels.getFormName())
			.tenantId("default")
			.category(String.valueOf(wflowModels.getGroupId()))
			.addString(wflowModels.getFormId() + ".bpmn", xmlString)
			.deploy();
		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
			.deploymentId(deploy.getId())
			.singleResult();
		WflowModels models = new WflowModels();
		BeanUtil.copyProperties(wflowModels, models);
		models.setProcessDefId(processDefinition.getId());
		models.setVersion(processDefinition.getVersion());
		models.setIsDelete(false);
		models.setIsStop(false);
		models.setDeployId(deploy.getId());
		models.setUpdated(new Date());
		models.setSort(0);
		modelsMapper.updateById(models);
		modelHistorysMapper.updateById(WflowModelHistorys.builder()
			.version(processDefinition.getVersion())
			.deployId(deploy.getId())
			.processDefId(processDefinition.getId())
			.id(wflowModels.getId())
			.build());
		// 解析配置的权限，进行拆分
		reloadModelsPerm(code, processNode);
		// 部署流程就是从历史表提取最新的流程
		log.info("部署流程{}成功，ID={}:", code, deploy.getId());
		return deploy.getId();
	}

	/**
	 * 解析表单发起权限控制设置
	 * @param code 表单流程编号
	 * @param processNode 节点数据
	 */
	private void reloadModelsPerm(String code, ProcessNode<?> processNode) {
		// 清空之前的权限然后重新设置
		modelPermsMapper.delete(new LambdaQueryWrapper<WflowModelPerms>().eq(WflowModelPerms::getFormId, code));
		List<OrgUser> assignedUser = ((RootProps) processNode.getProps()).getAssignedUser();
		if (CollectionUtil.isNotEmpty(assignedUser)) {
			Date time = GregorianCalendar.getInstance().getTime();
			// 开始解析所有选择的部门的子部门，深度遍历
			Set<String> deptSet = new HashSet<>();
			Set<String> userSet = new HashSet<>();
			assignedUser.forEach(org -> {
				if ("dept".equals(org.getType())) {
					deptSet.add(org.getId());
					deptSet.addAll(orgRepositoryService.getRecursiveSubDept(org.getId()));
				}
				else {
					userSet.add(org.getId());
				}
			});
			List<WflowModelPerms> modelPerms = deptSet.stream()
				.map(d -> WflowModelPerms.builder()
					.id(IdUtil.objectId())
					.formId(code)
					.orgId(d)
					.permType("dept")
					.createTime(time)
					.build())
				.collect(Collectors.toList());
			modelPerms.addAll(userSet.stream()
				.map(u -> WflowModelPerms.builder()
					.id(IdUtil.objectId())
					.formId(code)
					.orgId(u)
					.permType("user")
					.createTime(time)
					.build())
				.collect(Collectors.toList()));
			modelPermsMapper.insertBatch(modelPerms);
		}
	}

	@Override
	@Transactional
	public void delProcess(String code) {
		enableProcess(code, false);
		modelsMapper.deleteById(code);
	}

	@Override
	public WflowModelHistorys getLastVersionModel(String code) {
		List<WflowModelHistorys> historys = modelHistorysMapper
			.selectList(new LambdaQueryWrapper<WflowModelHistorys>().eq(WflowModelHistorys::getFormId, code)
				.orderByDesc(WflowModelHistorys::getVersion)
				.last("limit 1"));
		if (CollectionUtil.isNotEmpty(historys)) {
			return historys.get(0);
		}
		throw new BusinessException("未找到该流程");
	}

	@Override
	public CustomPrintConfigVo getCustomPrintConfig(String instanceId) {
		try {
			HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery()
				.processInstanceId(instanceId)
				.singleResult();
			WflowModelHistorys modelHistory = modelHistorysMapper
				.selectOne(new LambdaQueryWrapper<WflowModelHistorys>().select(WflowModelHistorys::getSettings)
					.eq(WflowModelHistorys::getProcessDefId, instance.getProcessDefinitionId())
					.eq(WflowModelHistorys::getVersion, instance.getProcessDefinitionVersion()));
			return JSONObject.parseObject(modelHistory.getSettings(), CustomPrintConfigVo.class);
		}
		catch (Exception e) {
			return new CustomPrintConfigVo();
		}
	}

	@Override
	public WflowModelHistorys getModelByDefId(String defId) {
		return modelHistorysMapper
			.selectOne(new LambdaQueryWrapper<WflowModelHistorys>().eq(WflowModelHistorys::getProcessDefId, defId));
	}

}
